
#define __USE_GNU

#include <ncurses.h>
#include <pthread.h>

#include "net.h"
#include "flow.h"
#include "config.h"
#include "state.h"

int8_t g_pause = 1;
int8_t g_state_ready = 0;

static char g_begin_run_time_str[64];
static char g_run_time_str[64];
time_t g_begin_run_time;

static void init_begin_time()
{
	static time_t current_time;
	struct tm *local_time;
	
	memset(g_begin_run_time_str, 0, 64);
	sprintf(g_begin_run_time_str, "Uninitialized time");
	current_time = time(NULL);
	if (current_time == ((time_t)-1)) {
		return;
	}

	local_time = localtime(&current_time);
	if (local_time == NULL) {
		return;
	}

	if (strftime(g_begin_run_time_str, sizeof(g_begin_run_time_str), "%Y-%m-%d %H:%M:%S", local_time) == 0) {
		return;
	}
	g_begin_run_time = mktime(local_time);
}

static void calc_run_time(const time_t* cur) {
	double diff;
	int d = 0,h = 0,m = 0,s = 0;
	
	diff = difftime(*cur, g_begin_run_time);
	if (diff >= 86400) {
		d = diff / 86400;
		diff -= (d * 86400);
	}
	if (diff >= 3600) {
		h = diff / 3600;
		diff -= (h * 3600);
	}
	if (diff >= 60) {
		m = diff / 60;
		diff -= (m * 60);
	}
	s = diff;
	sprintf(g_run_time_str, "        %02d %02d:%02d:%02d", d, h, m, s);
}

static char* get_current_time() {
	static time_t current_time;
	static char time_str[64];
	struct tm *local_time;
	time_t l_time;
	
	memset(time_str, 0, 64);
	current_time = time(NULL);
	if (current_time == ((time_t)-1)) {
		return "";
	}

	local_time = localtime(&current_time);
	if (local_time == NULL) {
		return "";
	}

	if (strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", local_time) == 0) {
		return "";
	}

	l_time = mktime(local_time);
	calc_run_time(&l_time);

	return time_str;
}    


static void update_current_state(p_current_state cs, uint64_t c, uint64_t err) {
	if (cs->count != 0) {
		cs->increase = (c - cs->count);
	}
	cs->count = c;
	cs->err = err;
}

void init_state(current_state s[])
{
	strcpy(s[0].name, "recv(byte)");
	s[0].count = 0;
	s[0].increase = 0;
	s[0].err = 0;
	s[0].speed = 0;
	s[0].unit_factor = 1024*1024;
	strcpy(s[1].name, "recv(pkt)");
	s[1].count = 0;
	s[1].increase = 0;
	s[1].err = 0;
	s[1].speed = 0; 
	s[1].unit_factor = 1;
	strcpy(s[2].name, "send(byte)");
	s[2].count = 0;
	s[2].increase = 0;
	s[2].err = 0;
	s[2].speed = 0;
	s[2].unit_factor = 1024*1024;
	strcpy(s[3].name, "send(pkt)");
	s[3].count = 0;
	s[3].increase = 0;
	s[3].err = 0;
	s[3].speed = 0;
	s[3].unit_factor = 1;
}

void handle_user_input(int ch, p_main_flow mflow, current_state cstate[]);
static void* status_thread(void* arg)
{
	p_main_flow mflow = (p_main_flow)arg;
	int scrLine, scrCol, i, k;
	struct timeval tv,now;
	double time_increase;
	//char mac_s[32] = {0};
	//char mac_d[32] = {0};
	int ch;
	
	(void)scrLine;
	(void)scrCol;

	while(!g_state_ready && !(mflow->m_bstop))
	{
		sleep(1);
	}

	current_state cstate[4] = {
		{
			"recv(byte)", 0, 0, 0, 0, 1024*1024
		},
		{
			"recv(pkt)", 0, 0, 0, 0, 1
		},
		{
			"send(byte)", 0, 0, 0, 0, 1024*1024
		},
		{
			"send(pkt)", 0, 0, 0, 0, 1
		}
	};
	init_state(cstate);
	init_begin_time();

	gettimeofday(&tv, NULL);
	initscr();
	cbreak();
	noecho();
	//keypad(stdscr, TRUE);
	//nodelay(stdscr, TRUE);
	timeout(800);
	k = 0;
	while (!(mflow->m_bstop)) {
		if ((ch = getch()) != ERR) {
			handle_user_input(ch, mflow, cstate);
		} else {
			usleep(1000000);
		}

		getmaxyx(stdscr, scrLine, scrCol);
		update_current_state(&cstate[0], mflow->m_flow_state.rx_byte, mflow->m_flow_state.err_rx_byte);
		update_current_state(&cstate[1], mflow->m_flow_state.rx_pkt, mflow->m_flow_state.err_rx_pkt);
		update_current_state(&cstate[2], mflow->m_flow_state.tx_byte, mflow->m_flow_state.err_tx_byte);
		update_current_state(&cstate[3], mflow->m_flow_state.tx_pkt, mflow->m_flow_state.err_tx_pkt);
		gettimeofday(&now, NULL);
		time_increase = (now.tv_sec-tv.tv_sec) + (now.tv_usec - tv.tv_usec)/1E6;
		tv=now;
		clear();
		k = 0;
		mvprintw(k++, 1, "recv/send(Mbyte/per); Speed(MByte/s);per second");
		for (i = 0; i < 4; i++){
			cstate[i].speed = cstate[i].increase / time_increase/cstate[i].unit_factor;
			mvprintw(k++, 1, "%16s: %16lld\tSpeed:%16.2f", cstate[i].name, cstate[i].count, cstate[i].speed); 
		}
		for (i = 0; i < 4; i++){
			mvprintw(k++, 1, "%12s-err: %16lld",cstate[i].name, cstate[i].err); 
		}
		if (mflow->m_flow_data_direct == flow_data_loop) {
			mvprintw(k++, 1, "transpond-delay/max(us): %ld/%ld", mflow->m_loop_delay, mflow->m_loop_delay_max);
		}
		mvprintw(k++, 1, "local-ip:%s", inet_ntoa(mflow->m_socket_io.local_addr.sin_addr));
		mvprintw(k++, 1, "remote-ip:%s", inet_ntoa(mflow->m_socket_io.remote_addr.sin_addr));
		//sprintf(mac_s, "%02X:%02X:%02X:%02X:%02X:%02X", mflow->m_socket_io.r_local_addr.sll_addr[0],
		//		mflow->m_socket_io.r_local_addr.sll_addr[1],
		//		mflow->m_socket_io.r_local_addr.sll_addr[2],
		//		mflow->m_socket_io.r_local_addr.sll_addr[3],
		//		mflow->m_socket_io.r_local_addr.sll_addr[4],
		//		mflow->m_socket_io.r_local_addr.sll_addr[5]);
		//sprintf(mac_d, "%02X:%02X:%02X:%02X:%02X:%02X", mflow->m_socket_io.r_remote_addr.sll_addr[0],
		//		mflow->m_socket_io.r_remote_addr.sll_addr[1],
		//		mflow->m_socket_io.r_remote_addr.sll_addr[2],
		//		mflow->m_socket_io.r_remote_addr.sll_addr[3],
		//		mflow->m_socket_io.r_remote_addr.sll_addr[4],
		//		mflow->m_socket_io.r_remote_addr.sll_addr[5]);
		//mvprintw(k++, 1, "local-mac:%s", mac_s);
		//mvprintw(k++, 1, "remote-mac:%s", mac_d);
		mvprintw(LINES-4, 0, "begin   :%s", g_begin_run_time_str);
		mvprintw(LINES-3, 0, "current :%s", get_current_time());
		mvprintw(LINES-2, 0, "total   :%s", g_run_time_str);
		mvaddstr(LINES-1, 0, "ctrl+c End run");
		refresh();
	}
	endwin();
	printf("status thread exit\n");
	update_current_state(&cstate[0], mflow->m_flow_state.rx_byte, mflow->m_flow_state.err_rx_byte);
	update_current_state(&cstate[1], mflow->m_flow_state.rx_pkt, mflow->m_flow_state.err_rx_pkt);
	update_current_state(&cstate[2], mflow->m_flow_state.tx_byte, mflow->m_flow_state.err_tx_byte);
	update_current_state(&cstate[3], mflow->m_flow_state.tx_pkt, mflow->m_flow_state.err_tx_pkt);
	for (i = 0; i < 4; i++){
		printf("%16s: %16ld\n", cstate[i].name, cstate[i].count);
	}
	if (mflow->m_flow_data_direct == flow_data_loop) {
		printf("transpond-delay/max(us): %ld/%ld\n", mflow->m_loop_delay, mflow->m_loop_delay_max);
	}
	printf("begin   :%s\n", g_begin_run_time_str);
	printf("current :%s\n", get_current_time());
	printf("total   :%s\n", g_run_time_str);
	pthread_exit(NULL);
}

static pthread_t status_th;

int start_state_monitor(p_main_flow mflow)
{
	pthread_attr_t th_attr;
	pthread_attr_init(&th_attr);
	pthread_attr_setdetachstate(&th_attr, PTHREAD_CREATE_JOINABLE);

	int ret = pthread_create(&status_th, NULL, status_thread, mflow);
	if (ret) {
		printf("ERROR2: return code from pthread_create() is %d\n", ret);
		return -1;
	}
	return 0;
}

void stop_state_monitor()
{
	pthread_join(status_th, NULL);
}

void handle_user_input(int ch, p_main_flow mflow, current_state cstate[])
{
	switch (ch) {
		case 'C':
			mflow->m_flow_state.rx_byte = 0;
			mflow->m_flow_state.rx_pkt  = 0;
			mflow->m_flow_state.tx_byte = 0;
			mflow->m_flow_state.tx_pkt  = 0;
			init_state(cstate);
			break;
		case 'P':
			g_pause = 0;
			break;
		case 'p':
			g_pause = 1;
			break;
	}
}

